/* Copyright (c) 2025 Beijing Semidrive Technology Corporation
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file  Dpe.c
 * @brief Semidrive. AUTOSAR 4.3.1 MCAL Dpe Driver.
 */
#ifdef __cplusplus
extern "C" {
#endif

#include "Det.h"
#include "Dpe.h"
#include "Mcal_MemLibc.h"
#include "irq_num.h"

/*########################Global variables########################*/

#define DPE_START_SEC_VAR_CLEARED_UNSPECIFIED
#include "Dpe_MemMap.h"

const Dpe_ConfigType *Dpe_pCurConfig;
/* PRQA S 1751 1 */
Dpe_ConfigType Dpe_DiagVcanCurConfig;
static Dpe_ControllerStateType Dpe_gCanDriverState;

#define DPE_STOP_SEC_VAR_CLEARED_UNSPECIFIED
#include "Dpe_MemMap.h"

#define DPE_START_SEC_CONST_UNSPECIFIED
#include "Dpe_MemMap.h"

static const Dpe_CanInfoType Dpe_CanInfo[16] =
{
    {0, 128, 0x800, 0x680},//canfd1
    {1, 128, 0x800, 0x680},
    {2, 128, 0x800, 0x680},
    {3, 128, 0x800, 0x680},
    {4, 128, 0x800, 0x680},
    {5, 128, 0x800, 0x680},
    {6, 128, 0x800, 0x680},
    {7, 128, 0x800, 0x680},
    {8, 128, 0x800, 0x680},
    {9, 128, 0x800, 0x680},
    {10, 128, 0x800, 0x680},
    {11, 64, 0x400, 0x280},//canfd11
    {12, 64, 0x400, 0x280},
    {13, 64, 0x400, 0x280},
    {14, 64, 0x400, 0x280},
    {15, 64, 0x400, 0x280}
};

/* PRQA S 1751 19 */
const uint32 Dpe_CanBaseAddrConfig[16] =
{
    0xF0580000,//canfd1
    0xF0590000,
    0xF8500000,
    0xF8230000,
    0xF8510000,
    0xF8240000,
    0xF8520000,
    0xF8250000,
    0xF8530000,
    0xF8260000,
    0xF8540000,
    0xF8270000,//canfd11
    0xF8550000,
    0xF8280000,
    0xF8560000,
    0xF8290000,
};

#define DPE_STOP_SEC_CONST_UNSPECIFIED
#include "Dpe_MemMap.h"

#define DPE_START_SEC_CODE
#include "Dpe_MemMap.h"

static Std_ReturnType Dpe_Ip_QueueConfig(void)
{
    Std_ReturnType errorId = E_OK;
    uint8 i;
    uint8 index;
    const Dpe_QueueConfigType *queueConfig;
    const Dpe_SubQueueTinyConfigType *queueTinyConfigType;
    const Dpe_CanInfoType * info = NULL;
    uint16 canChan = 0;
    Dpe_CanMbCfgType config;

    for (i = 0; i < Dpe_pCurConfig->queueTotalNum; i++)
    {
        queueConfig = &Dpe_pCurConfig->queueConfig[i];

        /* #60 config tx queue's mb mode and mb start address*/
        for (uint32_t i = 0; i < sizeof(Dpe_CanInfo) / sizeof(Dpe_CanInfoType); i++)
        {
            if (Dpe_CanInfo[i].index == queueConfig->txqueueIndex)
            {
                info = &Dpe_CanInfo[i];
                canChan = i;
                break;
            }
        }

        if (NULL_PTR == info)
        {
            errorId = DPE_E_ICOM_CONFIG_INVALID;
        }
        else
        {
            if (queueConfig->txqueueMbMode)
            {
                config.mbStartOffset = info->mbStartOffset2;
                config.mbMod = DPE_CAN_MB_MODE_64BYTES;
            }
            else
            {
                config.mbStartOffset = info->mbStartOffset1;
                config.mbMod = DPE_CAN_MB_MODE_8BYTES;
            }

            if (info->maxMbNum == 128)
            {
                config.mbSel = DPE_CAN_MB_SEL_21_27;
            }
            else
            {
                config.mbSel = DPE_CAN_MB_SEL_7_13;
            }

            DpeQueue_Lld_Config(Dpe_pCurConfig->base, canChan, &config);

            /* #61 config subqueue's replace whether enable and arbitration scheme*/
            for (index = 0; index < queueConfig->subqueueNum; index++)
            {
                queueTinyConfigType = &queueConfig->subQueueTinyConfig[index];

                if ((queueConfig->txqueueIndex < DPE_CAN_NUM) && (queueTinyConfigType->qid < DPE_SUBQUEUE_NUM))
                {
                    Dpe_Lld_ConfigSubqueue(Dpe_pCurConfig->base, queueConfig->txqueueIndex, queueTinyConfigType->qid,
                                           queueTinyConfigType->scheme, queueTinyConfigType->subqueueReplaceEn);

                    DpeQueue_Lld_Start(Dpe_pCurConfig->base, queueConfig->txqueueIndex);
                }
                else
                {
                    errorId = DPE_E_ICOM_CONFIG_INVALID;
                }
            }
        }

    }

    return errorId;
}

#if (CAN_SET_PFC_API == STD_ON)
static Std_ReturnType Dpe_Ip_PfcConfig(void)
{
    Std_ReturnType errorId = E_OK;
    const Dpe_PfcConfigType *pfcConfig = Dpe_pCurConfig->pfcConfig;
    const Dpe_PfcTinyConfigType *pfcTinyConfig;
    uint8 i;
    Dpe_PfcCfgType pfcCfg;

    /*mirror bus config*/
    if (NULL_PTR != pfcConfig->mirrorBusConfig)
    {
        Dpe_Lld_ConfigMirrorBus(Dpe_pCurConfig->base, pfcConfig->mirrorBusConfig->bus,
                                pfcConfig->mirrorBusConfig->subQid);
    }

    for (i = 0; i < pfcConfig->pfcTotalNum; i++)
    {
        pfcTinyConfig = &pfcConfig->pfcTinyConfig[i];
        Mcal_MemCpy(&pfcCfg, pfcTinyConfig->pfcCfg, sizeof(Dpe_PfcCfgType));
        DpePfc_Lld_Config(Dpe_pCurConfig->base, pfcTinyConfig->pfcIndex,
                          &pfcCfg);
        DpePfc_Lld_Enable(Dpe_pCurConfig->base, pfcTinyConfig->pfcIndex, TRUE);
    }

    return errorId;
}
#endif/*CAN_SET_PFC_API == STD_ON*/

#if (CAN_DIAGVCAN_API == STD_ON)
static Std_ReturnType Dpe_Ip_DiagVcanConfig(void)
{
    Std_ReturnType errorId = E_OK;

    if (NULL_PTR != Dpe_pCurConfig->pfcConfig->diagVcanCallback)
    {
        DpeVcan_Lld_EnableInt(Dpe_pCurConfig->base, DPE_DIAG_VCAN_CH_ID, 1 | 1 << 1);
    }

    DpeVcan_Lld_Config(Dpe_pCurConfig->base, DPE_DIAG_VCAN_CH_ID,
                       (uint32)Dpe_pCurConfig->pfcConfig->diagVcanRxFifo, FALSE);
    DpeVcan_Lld_Start(Dpe_pCurConfig->base, DPE_DIAG_VCAN_CH_ID, TRUE);

    return errorId;
}
#endif/*CAN_DIAGVCAN_API == STD_ON*/

/** *****************************************************************************************************
 * \brief This service initializes the DPE driver.
 *
 * \verbatim
 * Syntax             : Std_ReturnType Dpe_Init(const Dpe_ConfigType *Config)
 *
 * Service ID[hex]    : 0x00
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : mode - User mode (Non-Privileged mode).
 *
 * Parameters (in)    : Config - Dpe Configuration parameter.
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Driver Module Initialization function.The Initialization function shall
 *                      initialize DPE relevant registers with the values of the structure
 *                      referenced by the parameter configPtr
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
Std_ReturnType Dpe_Init(const Dpe_ConfigType *Config)
{
    Std_ReturnType errorId = E_OK;
    uint8 ch;

    /* #10 If the driver state is not NULL or Config is NULL PTR, report an error. */
    if (Config == NULL_PTR)
    {
        errorId = DPE_E_PARAM_POINTER;
    }

    if (E_OK == errorId)
    {

        Dpe_pCurConfig = Config;

        /* #20 If the Dpe_pCurConfig is INIT, report an error. */
        if (Dpe_gCanDriverState == DPE_INIT)
        {
            errorId = DPE_E_REINIT;
        }
        else
        {
            /* #30 config route table/route context table;*/
            Dpe_Lld_RoutingTableConfig(Dpe_pCurConfig->base, Dpe_pCurConfig->routeConfig->rtlbIndexAddr,
                                       Dpe_pCurConfig->routeConfig->indexSize, Dpe_pCurConfig->routeConfig->rtlbContextAddr,
                                       Dpe_pCurConfig->routeConfig->contextSize);

            /* #40 config can buffer base address (TX QUQUE's memeory address) */
            /* #50 config dpe accept which Can Controller dma quest signals；*/
            for (ch = 0; ch < DPE_CAN_NUM; ch++)
            {
                if (Dpe_pCurConfig->dpeCanMask & (1 << ch))
                {
                    Dpe_Lld_ConfigCan(Dpe_pCurConfig->base, ch, (uint32)Dpe_pCurConfig->canBufferBase[ch],
                                      Dpe_CanBaseAddrConfig[ch], TRUE);
                }
            }

            errorId = Dpe_Ip_QueueConfig();
#if (CAN_SET_PFC_API == STD_ON)
            errorId = Dpe_Ip_PfcConfig();
#endif/*CAN_SET_BAUDRATE_API == STD_ON*/
#if (CAN_DIAGVCAN_API == STD_ON)
            Mcal_MemCpy(&Dpe_DiagVcanCurConfig, Dpe_pCurConfig, sizeof(Dpe_ConfigType));
            errorId = Dpe_Ip_DiagVcanConfig();
#endif/*CAN_DIAGVCAN_API == STD_ON*/

            if (E_OK == errorId)
            {
                /* #70 dpe status from DPE_UNINIT to DPE_INIT */
                Dpe_gCanDriverState = DPE_INIT;

                /* #80 enable dpe */
                Dpe_Lld_Start(Dpe_pCurConfig->base);
            }

        }
    }

    /* ----- Development Error Report --------------------------------------- */
    if (E_OK != errorId)
    {
#if (STD_ON == DPE_DEV_ERROR_DETECT)
        (void)Det_ReportError((uint16)DPE_MODULE_ID, (uint8)DPE_INSTANCE, (uint8)DPE_SID_INIT,
                              (uint8)errorId);
#endif /** #if (STD_ON == DPE_DEV_ERROR_DETECT) */
    }/* else not needed */

    return errorId;
}

/** *****************************************************************************************************
 * \brief This service deinitializes the DPE driver.
 *
 * \verbatim
 * Syntax             : Std_ReturnType Dpe_DeInit(void)
 *
 * Service ID[hex]    : 0x01
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Non reentrant
 *
 * Parameters (in)    : mode - User mode (Non-Privileged mode).
 *
 * Parameters (in)    : None.
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : Driver Module DeInitialization function.The Initialization function shall
 *                      deinitialize DPE relevant registers and clear dpe state.
 * \endverbatim
 * Traceability       :
 *******************************************************************************************************/
Std_ReturnType Dpe_DeInit(void)
{
    Std_ReturnType errorId = E_OK;

    if (NULL_PTR == Dpe_pCurConfig)
    {
        errorId = DPE_E_PARAM_POINTER;
    }

    if (E_OK == errorId)
    {
        if (Dpe_gCanDriverState == DPE_UNINIT)
        {
            errorId = DPE_E_PARAM_POINTER;
        }
    }

    if (E_OK == errorId)
    {

        /*1. state from DPE_INIT to DPE_UNINIT；*/
        Dpe_gCanDriverState = DPE_UNINIT;

        /*2. DPE disable；*/
        Dpe_Lld_Stop(Dpe_pCurConfig->base);
    }

    /* ----- Development Error Report --------------------------------------- */
    if (E_OK != errorId)
    {
#if (STD_ON == DPE_DEV_ERROR_DETECT)
        (void)Det_ReportError((uint16)DPE_MODULE_ID, (uint8)DPE_INSTANCE, (uint8)DPE_SID_DEINIT,
                              (uint8)errorId);
#endif /** #if (STD_ON == DPE_DEV_ERROR_DETECT) */
    }/* else not needed */

    return errorId;
}

#if (CAN_SET_PFC_API == STD_ON)
uint16 Dpe_Pfc_GetCounter(uint8 PfcIdx)
{
    Std_ReturnType errorId = E_OK;
    uint32 counter = 0;

    /* #10 If the driver state is not NULL or Config is NULL PTR, report an error. */
    if (Dpe_pCurConfig == NULL_PTR)
    {
        errorId = DPE_E_PARAM_POINTER;
    }

    if (E_OK == errorId)
    {
        if (Dpe_gCanDriverState == DPE_UNINIT)
        {
            errorId = DPE_E_PARAM_POINTER;
        }
    }

    if (E_OK == errorId)
    {
        counter = DpePfc_Lld_GetCounter(Dpe_pCurConfig->base, PfcIdx);
    }

    /* ----- Development Error Report --------------------------------------- */
    if (E_OK != errorId)
    {
#if (STD_ON == DPE_DEV_ERROR_DETECT)
        (void)Det_ReportError((uint16)DPE_MODULE_ID, (uint8)DPE_INSTANCE, (uint8)DPE_SID_PFCGETCOUNT,
                              (uint8)errorId);
#endif /** #if (STD_ON == DPE_DEV_ERROR_DETECT) */
    }/* else not needed */

    return counter;
}
#endif/*CAN_SET_BAUDRATE_API == STD_ON*/

#if (CAN_DIAGVCAN_API == STD_ON)
static void DiagVCan_IP_CallBack(void *arg, Dpe_VcanStatusType status,
                                 DpeVcan_FrameType *frame, uint32 result)
{
    if (NULL_PTR != Dpe_pCurConfig->pfcConfig->diagVcanCallback)
    {
        Dpe_pCurConfig->pfcConfig->diagVcanCallback(arg, status);
    }
}

void Dpe_DiagVcanIrqHandler(void)
{
    irq_log_check_valid(DPE_DPE_DVCAN_INTR_NUM);
    DpeVcan_Lld_IrqHandler(Dpe_pCurConfig->base, DPE_DIAG_VCAN_CH_ID, DiagVCan_IP_CallBack,
                           &Dpe_DiagVcanCurConfig);
}
#endif/*end of CAN_DIAGVCAN_API*/

#define DPE_STOP_SEC_CODE
#include "Dpe_MemMap.h"

#ifdef __cplusplus
}
#endif
